agora inbox for [email protected]
help / color / mirror / Atom feedcache in plpgsql
320+ messages / 3 participants
[nested] [flat]
* cache in plpgsql
@ 2003-12-30 22:35 ivan <[email protected]>
2003-12-31 12:50 ` Re: cache in plpgsql Jan Wieck <[email protected]>
0 siblings, 1 reply; 320+ messages in thread
From: ivan @ 2003-12-30 22:35 UTC (permalink / raw)
To: pgsql-hackers
as new know plpgsql has special cache which remember too long (event
non-existing tables (i mean old oid)) so i suggest to create same function
only in plpgsql which would clear this cache, or sth like this ?
for ie, where i drop table i would do plpgsql_clear_cache ();
and when i will create one more time table with this same name plpgsql
will not remeber wrong oid
possible ?
^ permalink raw reply [nested|flat] 320+ messages in thread
* Re: cache in plpgsql
2003-12-30 22:35 cache in plpgsql ivan <[email protected]>
@ 2003-12-31 12:50 ` Jan Wieck <[email protected]>
2003-12-31 13:22 ` Re: cache in plpgsql ivan <[email protected]>
0 siblings, 1 reply; 320+ messages in thread
From: Jan Wieck @ 2003-12-31 12:50 UTC (permalink / raw)
To: ivan <[email protected]>; +Cc: pgsql-hackers
ivan wrote:
>as new know plpgsql has special cache which remember too long (event
>non-existing tables (i mean old oid)) so i suggest to create same function
>only in plpgsql which would clear this cache, or sth like this ?
>
>for ie, where i drop table i would do plpgsql_clear_cache ();
>and when i will create one more time table with this same name plpgsql
>will not remeber wrong oid
>
>possible ?
>
>
You obviously did not bother to search the archives on this.
This will not solve the problem since the "cache" you're talking about
is per backend local memory. So if one backend modifies the schema, how
does it cause all other to forgt? Since the same problem exists in
general for everything that uses SPI, the solution lies in there.
Jan
--
#======================================================================#
# It's easier to get forgiveness for being wrong than for being right. #
# Let's break this rule - forgive me. #
#================================================== [email protected] #
^ permalink raw reply [nested|flat] 320+ messages in thread
* Re: cache in plpgsql
2003-12-30 22:35 cache in plpgsql ivan <[email protected]>
2003-12-31 12:50 ` Re: cache in plpgsql Jan Wieck <[email protected]>
@ 2003-12-31 13:22 ` ivan <[email protected]>
2003-12-31 13:32 ` Re: cache in plpgsql Jan Wieck <[email protected]>
0 siblings, 1 reply; 320+ messages in thread
From: ivan @ 2003-12-31 13:22 UTC (permalink / raw)
To: Jan Wieck <[email protected]>; +Cc: pgsql-hackers
why all backend can not using one cache, which would be always
in real state ... or i can just clear only my cache, at first
(if i know that this relation could has another oid)
and then normal using relations ?
or ... just turn off cache, because its strange to has possible
using drop, create etc in function, but using only EXECUTE ..
there must be same solution .. no ?
On Wed, 31 Dec 2003, Jan Wieck wrote:
> ivan wrote:
>
> >as new know plpgsql has special cache which remember too long (event
> >non-existing tables (i mean old oid)) so i suggest to create same function
> >only in plpgsql which would clear this cache, or sth like this ?
> >
> >for ie, where i drop table i would do plpgsql_clear_cache ();
> >and when i will create one more time table with this same name plpgsql
> >will not remeber wrong oid
> >
> >possible ?
> >
> >
>
> You obviously did not bother to search the archives on this.
>
> This will not solve the problem since the "cache" you're talking about
> is per backend local memory. So if one backend modifies the schema, how
> does it cause all other to forgt? Since the same problem exists in
> general for everything that uses SPI, the solution lies in there.
>
>
> Jan
>
> --
>
> #======================================================================#
> # It's easier to get forgiveness for being wrong than for being right. #
> # Let's break this rule - forgive me. #
> #================================================== [email protected] #
>
>
>
>
> ---------------------------(end of broadcast)---------------------------
> TIP 3: if posting/reading through Usenet, please send an appropriate
> subscribe-nomail command to [email protected] so that your
> message can get through to the mailing list cleanly
>
^ permalink raw reply [nested|flat] 320+ messages in thread
* Re: cache in plpgsql
2003-12-30 22:35 cache in plpgsql ivan <[email protected]>
2003-12-31 12:50 ` Re: cache in plpgsql Jan Wieck <[email protected]>
2003-12-31 13:22 ` Re: cache in plpgsql ivan <[email protected]>
@ 2003-12-31 13:32 ` Jan Wieck <[email protected]>
2003-12-31 16:22 ` Re: cache in plpgsql ivan <[email protected]>
0 siblings, 1 reply; 320+ messages in thread
From: Jan Wieck @ 2003-12-31 13:32 UTC (permalink / raw)
To: ivan <[email protected]>; +Cc: pgsql-hackers
ivan wrote:
> why all backend can not using one cache, which would be always
Variable sized shared memory with garbage collection for SPI plans?
> in real state ... or i can just clear only my cache, at first
> (if i know that this relation could has another oid)
> and then normal using relations ?
As said, that is not sufficient. The user who does the DDL statement can
as well reconnect to the database to recompile all saved plans. It is
the 200 persistent PHP DB connections that suffer from not finding the
index any more.
>
> or ... just turn off cache, because its strange to has possible
> using drop, create etc in function, but using only EXECUTE ..
Do you have any numbers about how that would affect performance?
Jan
>
> there must be same solution .. no ?
>
>
> On Wed, 31 Dec 2003, Jan Wieck wrote:
>
>> ivan wrote:
>>
>> >as new know plpgsql has special cache which remember too long (event
>> >non-existing tables (i mean old oid)) so i suggest to create same function
>> >only in plpgsql which would clear this cache, or sth like this ?
>> >
>> >for ie, where i drop table i would do plpgsql_clear_cache ();
>> >and when i will create one more time table with this same name plpgsql
>> >will not remeber wrong oid
>> >
>> >possible ?
>> >
>> >
>>
>> You obviously did not bother to search the archives on this.
>>
>> This will not solve the problem since the "cache" you're talking about
>> is per backend local memory. So if one backend modifies the schema, how
>> does it cause all other to forgt? Since the same problem exists in
>> general for everything that uses SPI, the solution lies in there.
>>
>>
>> Jan
>>
>> --
>>
>> #======================================================================#
>> # It's easier to get forgiveness for being wrong than for being right. #
>> # Let's break this rule - forgive me. #
>> #================================================== [email protected] #
>>
>>
>>
>>
>> ---------------------------(end of broadcast)---------------------------
>> TIP 3: if posting/reading through Usenet, please send an appropriate
>> subscribe-nomail command to [email protected] so that your
>> message can get through to the mailing list cleanly
>>
--
#======================================================================#
# It's easier to get forgiveness for being wrong than for being right. #
# Let's break this rule - forgive me. #
#================================================== [email protected] #
^ permalink raw reply [nested|flat] 320+ messages in thread
* Re: cache in plpgsql
2003-12-30 22:35 cache in plpgsql ivan <[email protected]>
2003-12-31 12:50 ` Re: cache in plpgsql Jan Wieck <[email protected]>
2003-12-31 13:22 ` Re: cache in plpgsql ivan <[email protected]>
2003-12-31 13:32 ` Re: cache in plpgsql Jan Wieck <[email protected]>
@ 2003-12-31 16:22 ` ivan <[email protected]>
2003-12-31 18:36 ` Re: cache in plpgsql Jan Wieck <[email protected]>
0 siblings, 1 reply; 320+ messages in thread
From: ivan @ 2003-12-31 16:22 UTC (permalink / raw)
To: Jan Wieck <[email protected]>; +Cc: pgsql-hackers
but , all in all, do you think that now it is ok ?
On Wed, 31 Dec 2003, Jan Wieck wrote:
> ivan wrote:
> > why all backend can not using one cache, which would be always
>
> Variable sized shared memory with garbage collection for SPI plans?
>
> > in real state ... or i can just clear only my cache, at first
> > (if i know that this relation could has another oid)
> > and then normal using relations ?
>
> As said, that is not sufficient. The user who does the DDL statement can
> as well reconnect to the database to recompile all saved plans. It is
> the 200 persistent PHP DB connections that suffer from not finding the
> index any more.
>
> >
> > or ... just turn off cache, because its strange to has possible
> > using drop, create etc in function, but using only EXECUTE ..
>
> Do you have any numbers about how that would affect performance?
>
>
> Jan
>
> >
> > there must be same solution .. no ?
> >
> >
> > On Wed, 31 Dec 2003, Jan Wieck wrote:
> >
> >> ivan wrote:
> >>
> >> >as new know plpgsql has special cache which remember too long (event
> >> >non-existing tables (i mean old oid)) so i suggest to create same function
> >> >only in plpgsql which would clear this cache, or sth like this ?
> >> >
> >> >for ie, where i drop table i would do plpgsql_clear_cache ();
> >> >and when i will create one more time table with this same name plpgsql
> >> >will not remeber wrong oid
> >> >
> >> >possible ?
> >> >
> >> >
> >>
> >> You obviously did not bother to search the archives on this.
> >>
> >> This will not solve the problem since the "cache" you're talking about
> >> is per backend local memory. So if one backend modifies the schema, how
> >> does it cause all other to forgt? Since the same problem exists in
> >> general for everything that uses SPI, the solution lies in there.
> >>
> >>
> >> Jan
> >>
> >> --
> >>
> >> #======================================================================#
> >> # It's easier to get forgiveness for being wrong than for being right. #
> >> # Let's break this rule - forgive me. #
> >> #================================================== [email protected] #
> >>
> >>
> >>
> >>
> >> ---------------------------(end of broadcast)---------------------------
> >> TIP 3: if posting/reading through Usenet, please send an appropriate
> >> subscribe-nomail command to [email protected] so that your
> >> message can get through to the mailing list cleanly
> >>
>
>
> --
> #======================================================================#
> # It's easier to get forgiveness for being wrong than for being right. #
> # Let's break this rule - forgive me. #
> #================================================== [email protected] #
>
>
> ---------------------------(end of broadcast)---------------------------
> TIP 7: don't forget to increase your free space map settings
>
^ permalink raw reply [nested|flat] 320+ messages in thread
* Re: cache in plpgsql
2003-12-30 22:35 cache in plpgsql ivan <[email protected]>
2003-12-31 12:50 ` Re: cache in plpgsql Jan Wieck <[email protected]>
2003-12-31 13:22 ` Re: cache in plpgsql ivan <[email protected]>
2003-12-31 13:32 ` Re: cache in plpgsql Jan Wieck <[email protected]>
2003-12-31 16:22 ` Re: cache in plpgsql ivan <[email protected]>
@ 2003-12-31 18:36 ` Jan Wieck <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Jan Wieck @ 2003-12-31 18:36 UTC (permalink / raw)
To: ivan <[email protected]>; +Cc: pgsql-hackers
ivan wrote:
>
> but , all in all, do you think that now it is ok ?
No, I don't. I just prefer complete solutions over patchwork.
Jan
>
> On Wed, 31 Dec 2003, Jan Wieck wrote:
>
>> ivan wrote:
>> > why all backend can not using one cache, which would be always
>>
>> Variable sized shared memory with garbage collection for SPI plans?
>>
>> > in real state ... or i can just clear only my cache, at first
>> > (if i know that this relation could has another oid)
>> > and then normal using relations ?
>>
>> As said, that is not sufficient. The user who does the DDL statement can
>> as well reconnect to the database to recompile all saved plans. It is
>> the 200 persistent PHP DB connections that suffer from not finding the
>> index any more.
>>
>> >
>> > or ... just turn off cache, because its strange to has possible
>> > using drop, create etc in function, but using only EXECUTE ..
>>
>> Do you have any numbers about how that would affect performance?
>>
>>
>> Jan
>>
>> >
>> > there must be same solution .. no ?
>> >
>> >
>> > On Wed, 31 Dec 2003, Jan Wieck wrote:
>> >
>> >> ivan wrote:
>> >>
>> >> >as new know plpgsql has special cache which remember too long (event
>> >> >non-existing tables (i mean old oid)) so i suggest to create same function
>> >> >only in plpgsql which would clear this cache, or sth like this ?
>> >> >
>> >> >for ie, where i drop table i would do plpgsql_clear_cache ();
>> >> >and when i will create one more time table with this same name plpgsql
>> >> >will not remeber wrong oid
>> >> >
>> >> >possible ?
>> >> >
>> >> >
>> >>
>> >> You obviously did not bother to search the archives on this.
>> >>
>> >> This will not solve the problem since the "cache" you're talking about
>> >> is per backend local memory. So if one backend modifies the schema, how
>> >> does it cause all other to forgt? Since the same problem exists in
>> >> general for everything that uses SPI, the solution lies in there.
>> >>
>> >>
>> >> Jan
>> >>
>> >> --
>> >>
>> >> #======================================================================#
>> >> # It's easier to get forgiveness for being wrong than for being right. #
>> >> # Let's break this rule - forgive me. #
>> >> #================================================== [email protected] #
>> >>
>> >>
>> >>
>> >>
>> >> ---------------------------(end of broadcast)---------------------------
>> >> TIP 3: if posting/reading through Usenet, please send an appropriate
>> >> subscribe-nomail command to [email protected] so that your
>> >> message can get through to the mailing list cleanly
>> >>
>>
>>
>> --
>> #======================================================================#
>> # It's easier to get forgiveness for being wrong than for being right. #
>> # Let's break this rule - forgive me. #
>> #================================================== [email protected] #
>>
>>
>> ---------------------------(end of broadcast)---------------------------
>> TIP 7: don't forget to increase your free space map settings
>>
--
#======================================================================#
# It's easier to get forgiveness for being wrong than for being right. #
# Let's break this rule - forgive me. #
#================================================== [email protected] #
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
* [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW
@ 2024-07-26 21:33 Erik Wienhold <[email protected]>
0 siblings, 0 replies; 320+ messages in thread
From: Erik Wienhold @ 2024-07-26 21:33 UTC (permalink / raw)
This keeps the matview populated when replacing its definition.
---
.../sgml/ref/create_materialized_view.sgml | 15 ++++++++--
src/backend/commands/createas.c | 29 +++++++++++++------
src/backend/parser/gram.y | 16 ++++++++++
src/include/nodes/primnodes.h | 1 +
src/test/regress/expected/matview.out | 22 ++++++++++++++
src/test/regress/sql/matview.sql | 13 +++++++++
6 files changed, 84 insertions(+), 12 deletions(-)
diff --git a/doc/src/sgml/ref/create_materialized_view.sgml b/doc/src/sgml/ref/create_materialized_view.sgml
index 5e03320eb73..5b82af2ac70 100644
--- a/doc/src/sgml/ref/create_materialized_view.sgml
+++ b/doc/src/sgml/ref/create_materialized_view.sgml
@@ -27,7 +27,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
[ WITH ( <replaceable class="parameter">storage_parameter</replaceable> [= <replaceable class="parameter">value</replaceable>] [, ... ] ) ]
[ TABLESPACE <replaceable class="parameter">tablespace_name</replaceable> ]
AS <replaceable>query</replaceable>
- [ WITH [ NO ] DATA ]
+ [ WITH [ NO | OLD ] DATA ]
</synopsis>
</refsynopsisdiv>
@@ -37,7 +37,8 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
<para>
<command>CREATE MATERIALIZED VIEW</command> defines a materialized view of
a query. The query is executed and used to populate the view at the time
- the command is issued (unless <command>WITH NO DATA</command> is used) and may be
+ the command is issued (unless <command>WITH NO DATA</command> or
+ <command>WITH OLD DATA</command> is used) and may be
refreshed later using <command>REFRESH MATERIALIZED VIEW</command>.
</para>
@@ -162,7 +163,7 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
</varlistentry>
<varlistentry>
- <term><literal>WITH [ NO ] DATA</literal></term>
+ <term><literal>WITH [ NO | OLD ] DATA</literal></term>
<listitem>
<para>
This clause specifies whether or not the materialized view should be
@@ -170,6 +171,14 @@ CREATE [ OR REPLACE ] MATERIALIZED VIEW [ IF NOT EXISTS ] <replaceable>table_nam
flagged as unscannable and cannot be queried until <command>REFRESH
MATERIALIZED VIEW</command> is used.
</para>
+
+ <para>
+ The form <command>WITH OLD DATA</command> keeps the already stored data
+ when replacing an existing materialized view to keep it populated. Use
+ this form if you want to use <command>REFRESH MATERIALIZED VIEW CONCURRENTLY</command>
+ as it requires a populated materialized view. It is an error to use this
+ form when creating a new materialized view.
+ </para>
</listitem>
</varlistentry>
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 30ca0a21903..ac2491fe01c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -329,18 +329,26 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
/* An existing materialized view can be replaced. */
if (is_matview && into->replace)
{
- RefreshMatViewStmt *refresh;
-
/* Change the relation to match the new query and other options. */
- (void) create_ctas_nodata(query->targetList, into);
+ address = create_ctas_nodata(query->targetList, into);
+
+ /*
+ * Refresh the materialized view with a fake statement unless we
+ * must keep the old data.
+ */
+ if (!into->keepData)
+ {
+ RefreshMatViewStmt *refresh;
+
+ refresh = makeNode(RefreshMatViewStmt);
+ refresh->relation = into->rel;
+ refresh->skipData = into->skipData;
+ refresh->concurrent = false;
- /* Refresh the materialized view with a fake statement. */
- refresh = makeNode(RefreshMatViewStmt);
- refresh->relation = into->rel;
- refresh->skipData = into->skipData;
- refresh->concurrent = false;
+ address = ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ }
- return ExecRefreshMatView(refresh, pstate->p_sourcetext, qc);
+ return address;
}
return InvalidObjectAddress;
@@ -383,6 +391,9 @@ ExecCreateTableAs(ParseState *pstate, CreateTableAsStmt *stmt,
*/
if (is_matview)
{
+ if (into->keepData)
+ elog(ERROR, "must not specify WITH OLD DATA when creating a new materialized view");
+
do_refresh = !into->skipData;
into->skipData = true;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 7aaf0e37ad8..e51ce2701be 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4944,6 +4944,22 @@ CreateMatViewStmt:
$7->replace = true;
$$ = (Node *) ctas;
}
+ | CREATE OR REPLACE OptNoLog MATERIALIZED VIEW create_mv_target AS SelectStmt WITH OLD DATA_P
+ {
+ CreateTableAsStmt *ctas = makeNode(CreateTableAsStmt);
+
+ ctas->query = $9;
+ ctas->into = $7;
+ ctas->objtype = OBJECT_MATVIEW;
+ ctas->is_select_into = false;
+ ctas->if_not_exists = false;
+ /* cram additional flags into the IntoClause */
+ $7->rel->relpersistence = $4;
+ $7->skipData = false;
+ $7->keepData = true;
+ $7->replace = true;
+ $$ = (Node *) ctas;
+ }
;
create_mv_target:
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 3a0c3b06c81..e4c52ab05b4 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -169,6 +169,7 @@ typedef struct IntoClause
/* materialized view's SELECT query */
struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
+ bool keepData; /* true for WITH OLD DATA */
bool replace; /* replace existing matview? */
} IntoClause;
diff --git a/src/test/regress/expected/matview.out b/src/test/regress/expected/matview.out
index 56104b7ee8b..39b3a7c941c 100644
--- a/src/test/regress/expected/matview.out
+++ b/src/test/regress/expected/matview.out
@@ -731,6 +731,23 @@ SELECT * FROM mvtest_replace;
3
(1 row)
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 3
+(1 row)
+
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+ a
+---
+ 5
+(1 row)
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -899,3 +916,8 @@ ERROR: syntax error at or near "NOT"
LINE 1: CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_rep...
^
DROP MATERIALIZED VIEW mvtest_replace;
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
+ERROR: must not specify WITH OLD DATA when creating a new materialized view
diff --git a/src/test/regress/sql/matview.sql b/src/test/regress/sql/matview.sql
index fe0cb4d25bf..fb2e5f4b589 100644
--- a/src/test/regress/sql/matview.sql
+++ b/src/test/regress/sql/matview.sql
@@ -338,6 +338,14 @@ SELECT * FROM mvtest_replace; -- error: not populated
REFRESH MATERIALIZED VIEW mvtest_replace;
SELECT * FROM mvtest_replace;
+-- replace query but keep old data
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 5 AS a
+ WITH OLD DATA;
+SELECT * FROM mvtest_replace;
+REFRESH MATERIALIZED VIEW mvtest_replace;
+SELECT * FROM mvtest_replace;
+
-- add column
CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
SELECT 4 AS a, 1 b;
@@ -431,3 +439,8 @@ CREATE OR REPLACE MATERIALIZED VIEW IF NOT EXISTS mvtest_replace AS
SELECT 1 AS a;
DROP MATERIALIZED VIEW mvtest_replace;
+
+-- Clause WITH OLD DATA is not allowed when creating a new matview.
+CREATE OR REPLACE MATERIALIZED VIEW mvtest_replace AS
+ SELECT 17 AS a
+ WITH OLD DATA; -- error
--
2.50.1
--r33ycpluvyfavkwy--
^ permalink raw reply [nested|flat] 320+ messages in thread
end of thread, other threads:[~2024-07-26 21:33 UTC | newest]
Thread overview: 320+ messages (download: mbox mbox.gz follow: Atom feed)
-- links below jump to the message on this page --
2003-12-30 22:35 cache in plpgsql ivan <[email protected]>
2003-12-31 12:50 ` Jan Wieck <[email protected]>
2003-12-31 13:22 ` ivan <[email protected]>
2003-12-31 13:32 ` Jan Wieck <[email protected]>
2003-12-31 16:22 ` ivan <[email protected]>
2003-12-31 18:36 ` Jan Wieck <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
2024-07-26 21:33 [PATCH v7 3/3] Add WITH OLD DATA to CREATE OR REPLACE MATERIALIZED VIEW Erik Wienhold <[email protected]>
This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox